﻿using MQTTnet;
using MQTTnet.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using System.Threading.Tasks;

namespace TK.Mqtt.Client
{
    /// <summary>
    /// MqttClientWrapper
    /// </summary>
    public class MqttClientWrapper : IMqttClientWrapper
    {
        private readonly MqttClientConfig _config;
        private readonly IMqttClient _client;
        private MqttFactory _mqttFactory;
        private bool _isInit = false;
        private X509Certificate2 _cert;

        /// <summary>
        /// ctor
        /// </summary>
        public MqttClientWrapper(MqttClientConfig config)
        {
            _config = config;
            _mqttFactory = new MqttFactory();
            _client = _mqttFactory.CreateMqttClient();
        }

        /// <summary>
        /// 数据接收事件
        /// </summary>
        public event Action<CallbackData> ReceiveData;

        /// <summary>
        /// 启动服务
        /// </summary>
        public async Task StartAsync()
        {
            await Init();
        }

        /// <summary>
        /// 取消订阅
        /// </summary>
        public async Task UnSunscribeAsync(string topic)
        {
            try
            {
                MqttClientUnsubscribeOptionsBuilder builder = _mqttFactory.CreateUnsubscribeOptionsBuilder().WithTopicFilter(topic);
                await _client.UnsubscribeAsync(builder.Build(), System.Threading.CancellationToken.None);
            }
            catch (Exception e)
            {
                CallbackData data = new CallbackData { ClientConfig = _config, Type = DataType.UnSubscribe, Message = $"取消主题【{topic}】订阅错误：{e.Message}" };
                ReceiveData?.Invoke(data);
            }
        }

        /// <summary>
        /// disposable
        /// </summary>
        public void Dispose()
        {
            try
            {
                _client?.Dispose();
                _mqttFactory = null;
                _isInit = false;
                _cert?.Dispose();
            }
            catch { }
        }

        #region private method
        private async Task Init()
        {
            _client.ConnectedAsync += ClientConnectedAsync;
            _client.DisconnectedAsync += ClientDisconnectedAsync;
            _client.ApplicationMessageReceivedAsync += ClientApplicationMessageReceivedAsync;
            await ConnectAsync();
        }

        private async Task ConnectAsync()
        {
            MqttClientOptionsBuilder builder = _mqttFactory.CreateClientOptionsBuilder();
            builder.WithCleanSession();
            builder.WithWillQualityOfServiceLevel(_config.QOSLevel);
            builder.WithTcpServer(tcpOption =>
            {
                tcpOption.Server = _config.Host;
                tcpOption.Port = _config.Port;
            });
            if (!string.IsNullOrWhiteSpace(_config.ClientId))
            {
                builder.WithClientId(_config.ClientId);
            }
            else
            {
                builder.WithClientId(Guid.NewGuid().ToString("N"));
            }

            string user = _config.UserFunc == null ? _config.User : _config.UserFunc.Invoke();
            string password = _config.PasswordFunc == null ? _config.Password : _config.PasswordFunc.Invoke();
            if (!string.IsNullOrWhiteSpace(user))
            {
                builder.WithCredentials(user, password);
            }

            if (_config.IsTls)
            {
                try
                {
                    string certPath = Path.Combine(AppContext.BaseDirectory, _config.CertificatePath);
                    _cert = new X509Certificate2(certPath);
                    builder.WithTls((tls) =>
                    {
                        tls.UseTls = true;
                        tls.SslProtocol = _config.SslProtocol;
                        tls.Certificates = new List<X509Certificate> { _cert };
                        tls.CertificateValidationHandler = (validationArg) => true;
                    });
                }
                catch (Exception e)
                {
                    CallbackData data = new CallbackData { ClientConfig = _config, Type = DataType.Connect, Message = $"TLS安全证书错误：{e.Message}" };
                    ReceiveData?.Invoke(data);
                }
            }

            //可重新自定义MQTT相关连接
            _config.ConnectionAction?.Invoke(builder);
            MqttClientOptions option = builder.Build();

            try
            {
                MqttClientConnectResult connResult = await _client.ConnectAsync(option, System.Threading.CancellationToken.None);
                CallbackData data = new CallbackData { ClientConfig = _config, Type = DataType.Connect, Message = $"连接结果：{connResult.ResultCode.ToString() }" };
                ReceiveData?.Invoke(data);
            }
            catch (Exception e)
            {

                CallbackData data = new CallbackData { ClientConfig = _config, Type = DataType.Connect, Message = $"{(_isInit ? "重连" : "初次连接")}错误：{e.Message}" };
                ReceiveData?.Invoke(data);
            }

            await Task.CompletedTask;
        }

        private async Task SubscribeAsync()
        {
            try
            {
                MqttClientSubscribeOptionsBuilder builder = _mqttFactory.CreateSubscribeOptionsBuilder();
                _config.TopicList.ForEach((topic) =>
                {
                    builder.WithTopicFilter((topicFilter) =>
                    {
                        topicFilter.WithTopic(topic);
                        topicFilter.WithQualityOfServiceLevel(_config.QOSLevel);
                    });
                });
                MqttClientSubscribeOptions option = builder.Build();
                MqttClientSubscribeResult subcribeResult = await _client.SubscribeAsync(option, System.Threading.CancellationToken.None);
                CallbackData data = new CallbackData { ClientConfig = _config, Type = DataType.Connect, Message = $"订阅结果：{JsonSerializer.Serialize(subcribeResult)}" };
                ReceiveData?.Invoke(data);
            }
            catch (Exception e)
            {
                CallbackData data = new CallbackData { Type = DataType.Connect, Message = $"订阅初始化错误：{e.Message}" };
                ReceiveData?.Invoke(data);
            }
        }

        private async Task ClientConnectedAsync(MqttClientConnectedEventArgs arg)
        {
            await SubscribeAsync();
            CallbackData data = new CallbackData
            {
                ClientConfig = _config,
                Type = DataType.Connect,
                Message = "mqtt连接成功"
            };
            _isInit = true;
            ReceiveData?.Invoke(data);
        }

        private async Task ClientDisconnectedAsync(MqttClientDisconnectedEventArgs arg)
        {
            CallbackData data = new CallbackData
            {
                ClientConfig = _config,
                Type = DataType.Connect,
                Message = $"mqtt连接断开:{arg.ReasonString}->{arg.Exception?.Message}"
            };
            ReceiveData?.Invoke(data);
            if (_isInit)
            {
                System.Threading.SpinWait.SpinUntil(() => false, TimeSpan.FromSeconds(2));
                await ConnectAsync();
            }
        }

        private async Task ClientApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
        {
            CallbackData data = new CallbackData
            {
                ClientConfig = _config,
                Type = DataType.Subscribe,
                Topic = arg.ApplicationMessage.Topic,
                Data = arg.ApplicationMessage.Payload,
                Message = $"接收到数据：QOS:{arg.ApplicationMessage.QualityOfServiceLevel},retain:{arg.ApplicationMessage.Retain}"
            };
            _isInit = true;
            await Task.CompletedTask;
        }
        #endregion
    }
}